function defineReactive(data, key, val) {
    if (typeof val === 'object') { //1.所有属性都响应式
        observer(val);
    }
    let dep = new Dep();
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            dep.depend();
            return val;
        },
        set: function (newVal) {
            if (val === newVal) {
                return;
            }
            if (typeof newVal === 'object') {// 2.修改的值是对象
                observer(newVal);
            }
            val = newVal;
            dep.notify();
        }
    });
}

function observer(obj) {
    if (obj === null || typeof obj !== 'object') {
        return;
    }
    new Observer(obj);
}


function proxy(vm) {
    Object.keys(vm.$data).forEach((key) => {
        Object.defineProperty(vm, key, {
            get() {
                return vm.$data[key];
            },
            set(val) {
                vm.$data[key] = val;
            }
        });
    });


}

//4.对数组进行拦截

const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
[
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
].forEach(function (method) {
    // 缓存原始方法
    const original = arrayProto[method];
    Object.defineProperty(arrayMethods, method, {
            value: function mutator(...args) {
                console.log('修改数组---');
                return original.apply(this, args);
            },
            enumerable: false,
            writable: true,
            configurable: true
        }
    );
});

class Observer {
    constructor(value) {
        this.value = value;
        if (Array.isArray(value)) {
            value.__proto__ = arrayMethods;
        } else {
            this.walk(value);
        }
    }

    walk(obj) {
        Object.keys(obj).forEach((key) => {
            defineReactive(obj, key, obj[key]);
        });
    }
}

/*
1.对数据进行响应式处理，并代理到vue实例上。
2.进行模板解析--》初始化数据---》生成watcher--》自动添加到响应式数据的Dep里面。
3.数据更新的时候就会触发Dep里面的watcher执行--》更新视图
* */
class MiniVue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data;

        //1.对数据进行响应式处理
        observer(this.$data);

        //2.做一下数据代理，方便使用的时候，用app.,而不是app.data.
        proxy(this);

        //3.进行模板编译，生成watcher
        new Compile(options.el, this);
    }


}

/*
模板编译
1.处理差值表达式{{}}--->初始化
2.处理指令和事件--》mv-开头的，@开头的
3.以上两者的初始化和更新
* */
class Compile {
    constructor(el, vm) {
        this.$el = document.querySelector(el);
        this.$vm = vm;
        if (this.$el) {
            this.compile(this.$el);
        }
    }

    compile(el) {
        const childNodes = el.childNodes;
        childNodes.forEach(node => {
            if (node.nodeType === 1) {//元素
                //如果是元素的话，就要处理指令和事件 2.处理指令和事件--》v-开头的，@开头的
                const attrs = node.attributes;
                Array.from(attrs).forEach(attr => {
                    const attrName = attr.name;
                    const attrValue = attr.value;
                    if (attrName.startsWith('v-')) {
                        const dir = attrName.substring(2);
                        this[dir] && this[dir](node, attrValue);
                    }
                });

            } else if (this.isInter(node)) {//插值表达式
                this.compileText(node);
            }
            if (node.childNodes) {
                this.compile(node);
            }
        });
    }

    /* 1.处理差值表达式{{}} */
    isInter(node) {
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }

    /* 1.初始化 插值表达式*/
    compileText(node) {
        this.update(node,RegExp.$1,'');
    }
    Updater(node,value){
        node.textContent = value;
    }

    /* v-text 指令函数*/
    text(node, exp) {
        this.update(node,exp,'text');
    }
    textUpdater(node, value) {
        node.innerText = value;
    }

    /* v-html 指令函数*/
    html(node, exp) {
        this.update(node,exp,'html');
    }

    htmlUpdater(node, value) {
        node.innerHTML = value;
    }
    update(node,exp,dir){
        //1.初始化模板
        const fn = this[dir+'Updater'];
        fn && fn(node,this.$vm[exp]);
        //2.新建watcher
        new Watcher(this.$vm,exp,function (val){
            fn && fn(node,val);
        })
    }
}

/* 抽象一个watcher对象 */
class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm;
// 执行this.getter()，就可以读取data.a.b.c的内容
        this.getter = parsePath(expOrFn);
        this.cb = cb;
        this.value = this.get();
    }

    get() {
        window.target = this;
        let value = this.getter.call(this.vm, this.vm);
        window.target = undefined;
        return value;
    }

    update() {
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue);
    }
}

/**
 * 解析简单路径
 */
const bailRE = /[^\w.$]/;

function parsePath(path) {
    if (bailRE.test(path)) {
        return;
    }
    const segments = path.split('.');
    return function (obj) {
        for (let i = 0; i < segments.length; i++) {
            if (!obj) return;
            obj = obj[segments[i]];
        }
        return obj;
    };
}

/* 新建Dep */
class Dep {
    constructor() {
        this.subs = [];
    }

    addSub(sub) {
        this.subs.push(sub);
    }

    removeSub(sub) {
        remove(this.subs, sub);
    }

    depend() {
        if (window.target) {
            this.addSub(window.target);
        }
    }

    notify() {
        const subs = this.subs.slice();
        for (let i = 0, l = subs.length; i < l; i++) {
            subs[i].update();
        }
    }
}

function remove(arr, item) {
    if (arr.length) {
        const index = arr.indexOf(item);
        if (index > -1) {
            return arr.splice(index, 1);
        }
    }
}
